/*
 * Copyright (C) 2011 SANSOU SYSTEMS Co., Ltd. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package jp.co.sansou.android.atnd.util;

import jp.co.sansou.android.atnd.common.AtndConst;
import jp.co.sansou.android.atnd.common.AtndEnum;

import org.json.JSONObject;

public class AtndEventSearch extends AbsAtndEvent {

	private int[] eventId;

	private String[] keyword;

	private String[] keywordOr;

	private String[] ym;

	private String[] ymd;

	private int[] userId;

	private String[] nickname;

	private String[] twitterId;

	private int[] ownerId;

	private String[] ownerNickname;

	private String[] ownerTwitterId;

	private int start;

	private int count;

	private AtndEnum.Format format;

	/**
	 * ATNDのイベント検索に使用するキー項目を管理する列挙型です。
	 * 
	 * @author y-yamaguchi
	 */
	private static enum AtndEventSearchKeys {
		/** イベントID */
		EVENT_ID("event_id")
		/** キーワード（AND） */
		, KEYWORD("keyword")
		/** キーワード（OR） */
		, KEYWORD_OR("keyword_or")
		/** イベント開催年月 */
		, YM("ym")
		/** イベント開催年月日 */
		, YMD("ymd")
		/** 参加者のユーザID */
		, USER_ID("user_id")
		/** 参加者のニックネーム */
		, NICKNAME("nickname")
		/** 参加者のtwitter ID */
		, TWITTER_ID("twitter_id")
		/** 主催者のユーザID */
		, OWNER_ID("owner_id")
		/** 主催者のニックネーム */
		, OWNER_NICKNAME("owner_nickname")
		/** 主催者のtwitter ID */
		, OWNER_TWITTER_ID("owner_twitter_id")
		/** 検索の開始位置 */
		, START("start")
		/** 取得件数 */
		, COUNT("count")
		/** レスポンス形式 */
		, FORMAT("format");

		private String key;

		private AtndEventSearchKeys(String key) {
			this.key = key;
		}

		@Override
		public String toString() {
			return this.key;
		}
	}

	/**
	 * <pre>
	 * ATNDのイベントのレスポンスフィールド。
	 * キー「EVENT」はさらに内側にフィールドを配列で持っている。
	 * それらのキーの情報はAtndEventJsonSubKeys列挙型で定義している。
	 * </pre>
	 * 
	 * @author y-yamaguchi
	 */
	public static enum AtndEventJsonKeys {
		/** 含まれる検索結果の件数 */
		RESULTS_RETURNED("results_returned")
		/** 検索の開始位置 */
		, RESULTS_START("results_start")
		/**
		 * ATNDのイベント情報（複数要素） getJSONArrayメソッドで取得する時のキー。
		 */
		, EVENTS("events");

		private String key;

		private AtndEventJsonKeys(String key) {
			this.key = key;
		}

		@Override
		public String toString() {
			return this.key;
		}
	}

	/**
	 * AtndEventJsonKeysの「EVENT」内のサブキー項目です。
	 * 
	 * @author y-yamaguchi
	 */
	public static enum AtndEventJsonSubKeys {
		/** イベントID */
		EVENT_ID("event_id")
		/** タイトル */
		, TITLE("title")
		/** テーマ（キャッチ） */
		, THEME("catch")
		/** 概要 */
		, DESCRIPTION("description")
		/** ATNDのURL */
		, EVENT_URL("event_url")
		/** イベント開催日時（2009-06-10T19:00:00+09:00） */
		, STARTED_AT("started_at")
		/** イベント終了日時（2009-06-10T21:00:00+09:00） */
		, ENDED_AT("ended_at")
		/** 参考URL */
		, URL("url")
		/** 定員 */
		, LIMIT("limit")
		/** 開催場所 */
		, ADDRESS("address")
		/** 開催会場 */
		, PLACE("place")
		/** 開催会場の緯度 */
		, LAT("lat")
		/** 開催会場の経度 */
		, LON("lon")
		/** 主催者のID */
		, OWNER_ID("owner_id")
		/** 主催者のニックネーム */
		, OWNER_NICKNAME("owner_nickname")
		/** 主催者のtwitter ID */
		, OWNER_TWITTER_ID("owner_twitter_id")
		/** 参加者 */
		, ACCEPTED("accepted")
		/** 補欠者 */
		, WAITING("waiting")
		/** 更新日時（2009-06-04T11:56:20Z） */
		, UPDATED_AT("updated_at");

		private String subKey;

		private AtndEventJsonSubKeys(String subKey) {
			this.subKey = subKey;
		}

		@Override
		public String toString() {
			return this.subKey;
		}
	}

	/**
	 * <pre>
	 * ATNDのイベントを検索するための検索条件を設定するBuilderです。
	 * このクラスのbuild()メソッドを呼び出してAtndSearchEvent参照を取得して下さい。
	 * </pre>
	 * 
	 * @author y-yamaguchi
	 */
	public static class Builder {
		private int[] eventId;

		private String[] keyword;

		private String[] keywordOr;

		private String[] ym;

		private String[] ymd;

		private int[] userId;

		private String[] nickname;

		private String[] twitterId;

		private int[] ownerId;

		private String[] ownerNickname;

		private String[] ownerTwitterId;

		private int start = 1;

		private int count = 10; // 取得件数のデフォルトは10件（最大100件まで指定可能）

		private AtndEnum.Format format = AtndEnum.Format.JSON;

		/**
		 * <pre>
		 * イベント毎に割り当てられた番号で検索します。複数指定可能です。
		 * URLがhttp://atnd.org/events/757のイベントの場合、イベントIDは757になります。
		 * </pre>
		 * 
		 * @param eventId
		 *            イベントID
		 * @return Builder
		 */
		public Builder eventId(int... eventId) {
			this.eventId = eventId;
			return this;
		}

		/**
		 * <pre>
		 * イベントのタイトル、キャッチ、概要、住所をAND条件部分一致で検索します。
		 * 文字コードはUTF8。
		 * 複数指定可能です。
		 * </pre>
		 * 
		 * @param keyword
		 *            キーワード（AND）
		 * @return Builder
		 */
		public Builder keyword(String... keyword) {
			this.keyword = keyword;
			return this;
		}

		/**
		 * <pre>
		 * イベントのタイトル、キャッチ、概要、住所をOR条件部分一致で検索します。
		 * 文字コードはUTF8。
		 * 複数指定可能です。
		 * </pre>
		 * 
		 * @param keywordOr
		 *            キーワード（OR）
		 * @return Builder
		 */
		public Builder keywordOr(String... keywordOr) {
			this.keywordOr = keywordOr;
			return this;
		}

		/**
		 * <pre>
		 * 指定した年月に開催されているイベントを検索します。
		 * 複数指定可能です。
		 * yyyymm（例）200909で指定します。
		 * </pre>
		 * 
		 * @param ym
		 *            イベント開催年月
		 * @return Builder
		 */
		public Builder ym(String... ym) {
			this.ym = ym;
			return this;
		}

		/**
		 * <pre>
		 * 指定した年月日に開催されているイベントを検索します。
		 * 複数指定可能です。
		 * yyyymmdd （例）20090902で指定します。
		 * </pre>
		 * 
		 * @param ymd
		 *            イベント開催年月日
		 * @return Builder
		 */
		public Builder ymd(String... ymd) {
			this.ymd = ymd;
			return this;
		}

		/**
		 * <pre>
		 * 指定したユーザIDのユーザが参加しているイベントを検索します。複数指定可能です。
		 * URLがhttp://atnd.org/users/719の場合、ユーザIDは719になります。
		 * </pre>
		 * 
		 * @param userId
		 *            参加者のユーザID
		 * @return Builder
		 */
		public Builder userId(int... userId) {
			this.userId = userId;
			return this;
		}

		/**
		 * <pre>
		 * 指定したニックネームのユーザが参加しているイベントを検索します。
		 * 完全一致での検索のみ。使えないじゃん！！（ただし大文字，小文字の区別はなし）
		 * 文字コードはUTF。
		 * 複数指定可能です。
		 * </pre>
		 * 
		 * @param nickname
		 *            参加者のニックネーム
		 * @return Builder
		 */
		public Builder nickname(String... nickname) {
			this.nickname = nickname;
			return this;
		}

		/**
		 * <pre>
		 * 指定したTwitter IDのユーザが参加しているイベントを検索します。
		 * 完全一致での検索のみ。使えないじゃん！！（ただし大文字，小文字の区別はなし）
		 * 複数指定可能です。
		 * </pre>
		 * 
		 * @param twitterId
		 *            参加者のtwitter ID
		 * @return Builder
		 */
		public Builder twitterId(String... twitterId) {
			this.twitterId = twitterId;
			return this;
		}

		/**
		 * <pre>
		 * 指定したユーザIDのユーザが主催しているイベントを検索します。
		 * 複数指定可能です。
		 * </pre>
		 * 
		 * @param ownerId
		 *            主催者のユーザID
		 * @return Builder
		 */
		public Builder ownerId(int... ownerId) {
			this.ownerId = ownerId;
			return this;
		}

		/**
		 * <pre>
		 * 指定したニックネームのユーザが主催しているイベントを検索します。
		 * 完全一致での検索のみ。使えないじゃん！！（ただし大文字，小文字の区別はなし）
		 * 文字コードはUTF。
		 * 複数指定可能です。
		 * </pre>
		 * 
		 * @param ownerNickname
		 *            主催者のニックネーム
		 * @return Builder
		 */
		public Builder ownerNickname(String... ownerNickname) {
			this.ownerNickname = ownerNickname;
			return this;
		}

		/**
		 * <pre>
		 * 指定したtwitter IDのユーザが主催しているイベントを検索します。
		 * 完全一致での検索のみ。使えないじゃん！！（ただし大文字，小文字の区別はなし）
		 * 複数指定可能です。
		 * </pre>
		 * 
		 * @param ownerTwitterId
		 *            主催者のtwitter ID
		 * @return Builder
		 */
		public Builder ownerTwitterId(String... ownerTwitterId) {
			this.ownerTwitterId = ownerTwitterId;
			return this;
		}

		/**
		 * <pre>
		 * 検索結果の何件目から出力するかを指定します。
		 * 初期値は「1」です。
		 * </pre>
		 * 
		 * @param start
		 *            検索の開始位置
		 * @return Builder
		 */
		public Builder start(int start) {
			this.start = start;
			return this;
		}

		/**
		 * <pre>
		 * 検索結果の最大出力データ数を指定します。
		 * 初期値：10、最小値：1、最大値：100です。
		 * </pre>
		 * 
		 * @param count
		 *            取得件数
		 * @return Builder
		 */
		public Builder count(int count) {
			if (count > 100) {
				this.count = 100;
			} else if (count < 1) {
				this.count = 1;
			} else {
				this.count = count;
			}
			return this;
		}

		/**
		 * <pre>
		 * レスポンスをXMLかJSONかJSONPかを指定します。
		 * JSONPの場合、さらにパラメータ callback=コールバック関数名を指定する事により、
		 * javascript側コールバック関数の名前を指定できます。
		 * ここで指定する値はAtndSearchEvent.Format列挙型を指定して下さい。
		 * </pre>
		 * 
		 * @param format
		 *            レスポンス形式（AtndEnum.Format列挙型）
		 * @return Builder
		 */
		public Builder format(AtndEnum.Format format) {
			this.format = format;
			return this;
		}

		/**
		 * 検索条件を設定し、AtndSearchEventオブジェクトを取得します。
		 * 
		 * @return AtndSearchEvent
		 */
		public AtndEventSearch build() {
			return new AtndEventSearch(this);
		}
	}

	/**
	 * <pre>
	 * AtndSearchEventクラスのprivateなコンストラクタです。
	 * </pre>
	 * 
	 * @param builder
	 *            AtndSearchEvent.Builderオブジェクト
	 */
	private AtndEventSearch(Builder builder) {
		eventId = builder.eventId;
		keyword = builder.keyword;
		keywordOr = builder.keywordOr;
		ym = builder.ym;
		ymd = builder.ymd;
		userId = builder.userId;
		nickname = builder.nickname;
		twitterId = builder.twitterId;
		ownerId = builder.ownerId;
		ownerNickname = builder.ownerNickname;
		ownerTwitterId = builder.ownerTwitterId;
		start = builder.start;
		count = builder.count;
		format = builder.format;
	}

	/**
	 * <pre>
	 * ATNDより検索条件に一致するイベント情報一覧をJSON形式（JSONObject型）で取得します。
	 * JSONObjectの中身を取得するためのキー項目は「AtndEventJsonKeys」列挙型で管理されてます。
	 * </pre>
	 * 
	 * @return
	 */
	public JSONObject searchEventResultJson() {
		return super.searchAndReturnJson(makeSearchAtndEventUri());
	}

	/**
	 * ATNDイベントの検索用URIを生成し返します。
	 * 
	 * @return ATNDイベント検索用URI
	 */
	private String makeSearchAtndEventUri() {
		StringBuilder sb = new StringBuilder();
		sb.append(AtndConst.ATND_EVENTS_URL);
		sb.append("?");
		sb.append(AtndEventSearchKeys.COUNT + "=" + this.count);
		sb.append(getAppendParamStr(AtndEventSearchKeys.START, this.start));
		sb.append(getAppendParamStr(AtndEventSearchKeys.FORMAT, this.format));

		if (isSettingIntParam(eventId)) {
			sb.append(getAppendParamStr(AtndEventSearchKeys.EVENT_ID,
					toCsvInt(eventId)));
		}

		// KEYWORDのAND検索またはOR検索
		if (isSettingStrParam(keyword)) {
			sb.append(getAppendParamStr(AtndEventSearchKeys.KEYWORD,
					toCsv(keyword)));
		} else if (isSettingStrParam(keywordOr)) {
			sb.append(getAppendParamStr(AtndEventSearchKeys.KEYWORD_OR,
					toCsv(keywordOr)));
		}

		// イベント開催年月または年月日の検索
		if (isSettingStrParam(ym)) {
			sb.append(getAppendParamStr(AtndEventSearchKeys.YM, toCsv(ym)));
		} else if (isSettingStrParam(ymd)) {
			sb.append(getAppendParamStr(AtndEventSearchKeys.YMD, toCsv(ymd)));
		}

		if (isSettingIntParam(userId)) {
			sb.append(getAppendParamStr(AtndEventSearchKeys.USER_ID,
					toCsvInt(userId)));
		}

		if (isSettingStrParam(nickname)) {
			sb.append(getAppendParamStr(AtndEventSearchKeys.NICKNAME,
					toCsv(nickname)));
		}

		if (isSettingStrParam(twitterId)) {
			sb.append(getAppendParamStr(AtndEventSearchKeys.TWITTER_ID,
					toCsv(twitterId)));
		}

		if (isSettingIntParam(ownerId)) {
			sb.append(getAppendParamStr(AtndEventSearchKeys.OWNER_ID,
					toCsvInt(ownerId)));
		}

		if (isSettingStrParam(ownerNickname)) {
			sb.append(getAppendParamStr(AtndEventSearchKeys.OWNER_NICKNAME,
					toCsv(ownerNickname)));
		}

		if (isSettingStrParam(ownerTwitterId)) {
			sb.append(getAppendParamStr(AtndEventSearchKeys.OWNER_TWITTER_ID,
					toCsv(ownerTwitterId)));
		}

		System.out.println(sb.toString());

		return sb.toString();
	}
}
